
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

using Swaf.BizObj;
using Swaf.Container;
using Swaf.Logging;
using Swaf.Security;

namespace Swaf.Gui.AspNet2
{
	public class BasePage : System.Web.UI.Page, IPage
	{
		protected ServerControlLocalizer m_localizer = new ServerControlLocalizer();
		protected BizProcCoordinator m_bpc = null;
		protected string m_securityAppPrivilege = null;
		protected Privileges m_requiredAccess = Privileges.NotSet;

		protected override void OnPreInit(EventArgs e)
		{
			BaseBusinessProcess.resetShowDataCounts();
			if (Swaf.Application.globalApp == null && ViewName.ToLower() != "systemerror")
				Response.Redirect("SystemError.aspx");

			base.OnPreInit(e);
			setupChildSkins(this);
			if (CurrentUser != null && CurrentUser.containsField("settings.themeName"))
			{
				string themeName = CurrentUser.get("settings.themeName") as string;
				if (!Cmn.IsEmpty(themeName))
					Page.Theme = themeName;
				string masterPageThemeName = themeName;
				int subIndex = masterPageThemeName.IndexOf("-");
				if (subIndex != -1)
					masterPageThemeName = masterPageThemeName.Substring(0, subIndex);
				string masterPage = Page.MasterPageFile;
				if (!Cmn.IsEmpty(masterPage))
				{

					int p = masterPage.LastIndexOf('/');
					Debug.Assert(p != -1);
					masterPage = masterPage.Substring(p + 1);
					masterPage = string.Format("/App_ThemeMasters/{0}/{1}", masterPageThemeName, masterPage);
					Page.MasterPageFile = masterPage;
				}
			}
		}

		/// <summary>
		/// Process url is a chance for the page itself to modify a url before it is sent to the browser.
		/// this is used for things like apply the app path modifier (so the session id can be used in the url) or
		/// changing the link to use ssl.
		/// </summary>
		/// <param name="url"></param>
		/// <returns></returns>
		public virtual string processUrl(string url)
		{
			return Page.Response.ApplyAppPathModifier(url);
		}

		static Type[] s_emptyTypes = { };
		static object[] s_emptyObjs = { };
		protected void setupChildSkins(Control ctl)
		{
			foreach (Control childCtl in ctl.Controls)
			{
				if (childCtl.GetType().GetMethod("setupSkins") != null)
					Cmn.callMethod(childCtl, "setupSkins", s_emptyTypes, s_emptyObjs);
				if (childCtl.Controls.Count > 0)
					setupChildSkins(childCtl);
			}
		}

		public IWidget findWidget(string widgetId)
		{
			Control ctl = findControl(this, widgetId);
			return ctl as IWidget;
		}

		protected string callRule(string ruleName, params object[] p)
		{
			string retVal = "";
			IResults rc = App.ruleMgr.execute(ruleName, p);
			if (rc.CallResults != null)
				retVal = rc.CallResults.ToString();
			return retVal;
		}

		protected Control findControl(Control parent, string id)
		{
			Control ctl = null;

			foreach(Control childCtl in parent.Controls)
				if (childCtl.ID != null && childCtl.ID.CompareTo(id) == 0)
				{
					ctl = childCtl;
					break;
				}

			if (ctl == null)
				foreach (Control childCtl in parent.Controls)
					if ((ctl = findControl(childCtl, id)) != null)
						break;
			return ctl;
		}

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);

			object ucu = App.sessionData["usingCompanyUrl"];
			IResults rc = App.ruleMgr.execute("checkCurrentUserAgainstUiViewName", Page.GetType().Name);
			if (rc.CallResults is bool && (bool)rc.CallResults)
			{
				string localPath = Request.Url.PathAndQuery;
				if (App.sessionData["usingCompanyUrl"] is bool && (bool)App.sessionData["usingCompanyUrl"])
				{
					int inx = localPath.IndexOf('/', 1);
					if (inx != -1)
						localPath = localPath.Substring(inx);
				}
				App.Log.println(LoggingLevels.Medium, "About to redirect {0}:{1}", Request.Url.PathAndQuery, localPath);
				Response.Redirect(localPath);
			}

			if (BPC == null)
				throw new BaseException("Page must be have bizproccoordinator");

			if (!Cmn.IsTrue(App.sessionData["NoCulture"]) && !Cmn.IsTrue(App.sessionData["UserSelectedCulture"]))
			{
				if (((Swaf.BizRule.IRuleManagerEx)App.ruleMgr).getRuleInfo("getAllTranslations", false) == null)
				{
					App.sessionData["NoCulture"] = true;
				}
				else
				{
                    Swaf.BGuid.Guid defaultTranslationId = ActiveCompany.get("settings.translationId") as Swaf.BGuid.Guid;
					rc = App.ruleMgr.execute("getAllTranslations");
					IBizObjList translations = rc.CallResults as IBizObjList;
                    if (translations != null)
                    {
                        IBizObj translation = null;
                        if(Request.UserLanguages != null)
                            foreach (string cul in Request.UserLanguages)
                            {
                                string lang = cul.Split('-')[0].ToLower();
                                translation = translations.search("language.iso6391", lang);
                                if (translation != null)
                                    break;
                            }

                        if (translation == null && defaultTranslationId != null)
                            translation = translations.search("id", defaultTranslationId);

                        if (translation != null)
                        {
                            IBizObj culture = App.bizObjMgr.create("Culture");
                            string dotNetCulture = (string)translation.get("language.iso6391");
                            if (dotNetCulture == "zh")
                                dotNetCulture = "zh-CHS";
                            else if (dotNetCulture == "ko")
                                dotNetCulture = "ko-KR";
                            else if (dotNetCulture == "ja")
                                dotNetCulture = "ja-JP";
                            culture.put("dotNetBaseCulture", dotNetCulture);
                            culture.put("browserCode", translation.get("language.iso6391"));
                            culture.put("translationId", translation.get("id"));
                            App.resMgr.CurrentCulture = culture;
                        }
                    }
				}
			}
		}

		protected string SecurityPrivilege { get { return m_securityAppPrivilege; } set { m_securityAppPrivilege = value; } }
		protected Privileges RequiredPrivilegeLevel { get { return m_requiredAccess == Privileges.NotSet ? Privileges.ReadOnly : m_requiredAccess; } set { m_requiredAccess = value; } }

		private void localizeAndResolveControls(ControlCollection controls, bool doLocalization)
		{
			foreach (Control ctl in controls)
			{
				string typeName = ctl.GetType().FullName;
				if (typeName.ToLower().Contains("menu"))
				{
					typeName = typeName;
				}
				//if (ctl is MasterPage || ctl is ContentPlaceHolder || ctl is HtmlForm || ctl is Swaf.Gui.AspNet2.UserControl)
				if (ctl.Controls.Count > 0)
					localizeAndResolveControls(ctl.Controls, doLocalization);
				else if (!(ctl is LiteralControl) && ctl.ID != null)
				{
					//Do resolve of various types of control values before doing localization
					//if (ctl.ID[0] == '_')
					{
						HyperLink hctl = ctl as HyperLink;
						if (hctl != null)
							hctl.NavigateUrl = App.resMgr.resolve(hctl.NavigateUrl);

						if (doLocalization)
							m_localizer.localizeControl(ctl, this, "");
					}
				}

				HyperLink hctl2 = ctl as HyperLink;
				if (hctl2 != null)
					hctl2.NavigateUrl = processUrl(hctl2.NavigateUrl);
			}
		}


		static Regex s_bizProcRE = new Regex(@"\$(\w+)\.");
		protected override void OnPreRender(EventArgs e)
		{
			string currentUserName = "!No CurrentUser!";
			if (CurrentUser != null)
				currentUserName = (string)CurrentUser.get("userName");

			App.Trace.write(TraceLevels.Normal, "<PageRequest sessionId='{0}' userId='{1}' viewName='{2}' userIP='{3}' userClient='{4}' />",
				App.sessionId, currentUserName, ViewName, Request.UserHostAddress, Request.UserAgent);

			//Because going through the initial list of active bizprocs could add more to the list, we must actually
			//make a seperate list and go through that one first and force show and then again to catch
			//the extra ones that didnt show yet.
			Dictionary<string, ActiveBizProc> bpcs = new Dictionary<string, ActiveBizProc>();
			cloneListAndShowData(bpcs);
			cloneListAndShowData(bpcs);
			cloneListAndShowData(bpcs);
			cloneListAndShowData(bpcs);
			/*
			foreach (ActiveBizProc b in BPC.ActiveBizProcs.Values)
				bpcs.Add(b);
			foreach(ActiveBizProc b in bpcs)
				if(b.BizProc.Id != "_all")
					b.BizProc.showData(ForceLevel.mustShowGetAllDataIfNeeded);

			foreach(ActiveBizProc b in BPC.ActiveBizProcs.Values)
				if(!bpcs.Contains(b) && b.BizProc.Id != "_all")
					b.BizProc.showData(ForceLevel.mustShowGetAllDataIfNeeded);
			*/
			foreach(BizProcOfflineState offline in BPC.PageState.BizProcStates.Values)
				foreach(List<ParentBizProcInfo> pinfo in offline.ParentBizProcs.Values)
					if (pinfo[0].ParentId == "_all")
					{
						ActiveBizProc b = BPC.getActiveBizProc(offline.Id);
						b.BizProc.showData(ForceLevel.showIfHasData);
					}

			object nop = Swaf.Cmn.callPropertyGet(this, "TitleFormat", false);
			if (nop != null && nop is string)
			{
				FlexiMap m = new FlexiMap();
				m.put("company", ActiveCompany);
				MatchCollection mc = s_bizProcRE.Matches((string)nop);
				foreach (Match rm in mc)
				{
					string bpId = rm.Groups[1].Value;
					if (BPC.ActiveBizProcs.ContainsKey(bpId))
						m.put(bpId, BPC.ActiveBizProcs[bpId].BizProc.CurrentBizObj);
				}
				Title = App.resMgr.resolve((string)nop, m);
			}
			base.OnPreRender(e);
		}

		protected void cloneListAndShowData(Dictionary<string, ActiveBizProc> bpcs)
		{
			//Because going through the initial list of active bizprocs could add more to the list, we must actually
			//make a seperate list and go through that one first and force show and then again to catch
			//the extra ones that didnt show yet.
			Dictionary<string, ActiveBizProc> cloned = new Dictionary<string, ActiveBizProc>();
			foreach (ActiveBizProc b in BPC.ActiveBizProcs.Values)
				cloned[b.BizProc.Id] = b;
			foreach (ActiveBizProc b in cloned.Values)
				if (!bpcs.ContainsKey(b.BizProc.Id) && b.BizProc.Id != "_all")
				{
					bpcs[b.BizProc.Id] = b;
					b.BizProc.showData(ForceLevel.mustShowGetAllDataIfNeeded);
				}
		}

		protected override void Render(HtmlTextWriter writer)
		{
			bool doLocalization = !Cmn.IsTrue(App.sessionData["NoCulture"]);
			localizeAndResolveControls(Controls, doLocalization);

			if (m_localizer != null && m_localizer.UiViewLocalizationInfo != null && m_localizer.ShouldTrackPageFields)
			{
				IResults rc = App.ruleMgr.execute("addApplicationView", m_localizer.UiViewLocalizationInfo.find(m_localizer.calcPageName(this), false));
			}
            foreach(Control ctrl in Header.Controls)
                if (ctrl is HtmlLink)
                {
                    HtmlLink hl = (HtmlLink)ctrl;
                    if (hl.Href.StartsWith("~/App_Themes"))
                        hl.Href = hl.Href.Substring(1);
                }
			base.Render(writer);
			string msg = "Invalid SecurityAttribute";
			string method = string.Format("{0}.OnLoad()", GetType().Name);
			object[] attrs = GetType().GetCustomAttributes(typeof(SecurityAttribute), true);
			decimal clientId = 0;
			decimal accountId = -1;

			if (!Cmn.IsEmpty(m_securityAppPrivilege) || (attrs != null && attrs.Length != 0 && attrs[0] is SecurityAttribute))
			{
				string privStr = m_securityAppPrivilege;
				if (privStr == null)
				{
					SecurityAttribute sattr = (SecurityAttribute)attrs[0];
					privStr = sattr.ResourceName;
				}
				Privileges priv = App.securityMgr.currentPrincipal.getPrivilege(App.resMgr.resolve(privStr));
				IBizObj user = App.securityMgr.currentPrincipal.additionalInfo as IBizObj;
				if (user == null)
				{
					Response.Redirect(App.resMgr.resolve("$RedirectForPageAccessSecurityFailure$"));
					return;
				}
				if (priv < RequiredPrivilegeLevel)
				{

					msg = string.Format("Invalid Page Access User Priv : {0}. Required Access: {1}", priv.ToString(), RequiredPrivilegeLevel.ToString());
					//Syrinx.AuditLog.log((int)LoggingLevels.High, this.PageKey, msg, method, "ClientManagementBizRule", null, null, null, null, clientId, accountId, accountId);
					//Syrinx.AuditLog.audit((int)LoggingLevels.High, "APPINVLDCRED", this.PageKey, priv.ToString(), null, clientId, accountId, accountId, null);

					//redirectWithMessage("ContactUs.aspx", "Invalid security Access", 36);
					if(Cmn.IsTrue(user.get("authenticated")))
						Response.Redirect(App.resMgr.resolve("$RedirectForPageAccessSecurityFailure$"));
					else
					{
						App.sessionData.put("RedirectAfterLogin", Request.Url.ToString());
						Response.Redirect(App.resMgr.resolve("$RedirectSecurityFailureNotAuthenticated$"));
					}
				}
			}
		}

		protected override void OnUnload(EventArgs e)
		{
			base.OnUnload(e);
		}

		public string ViewName 
		{
			get 
			{
				string vName = null;
				try { vName = Request["oldPath"]; }catch(Exception){}
				if (Cmn.IsEmpty(vName))
				{
					string n = Page.GetType().Name;
					StringBuilder name = new StringBuilder(n);
					name[n.LastIndexOf('_')] = '.';
					name.Replace('_', '/');
					vName = name.ToString();
				}
				else
					vName = vName.Trim('/').ToLower();
				return vName;
			}
		}
		public void gotoView(string viewName)
		{
			Response.Redirect(processUrl(viewName));
		}

		public BizProcCoordinator BPC
		{
			get
			{
				if (m_bpc == null)
				{
					bool clearState = !IsPostBack && !Page.GetType().Name.EndsWith("child_aspx");
					ViewOfflineState pageState = PageStateManager.getState(ViewName,clearState);
					m_bpc = new BizProcCoordinator(this, pageState);
				}
				return m_bpc;
			}
			set
			{
				m_bpc = value;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="resourceName"></param>
		/// <returns></returns>
		public Privileges currentPrincipalPrivilege(string resourceName)
		{
			Privileges priv = Privileges.ReadOnly;
			if (resourceName != null)
				priv = App.securityMgr.currentPrincipal.getPrivilege(resourceName);
			return priv;
		}

		public IBizObj CurrentUser { get { return App.securityMgr.currentPrincipal.additionalInfo as IBizObj; } }
		public Swaf.BGuid.Guid CurrentCompanyId
		{
			get
			{
				Debug.Assert(CurrentUser.get("companyId") != null);
				Debug.Assert(CurrentUser.get("companyId") is Swaf.BGuid.Guid);
				return (Swaf.BGuid.Guid)CurrentUser.get("companyId");
			}
		}

		public Swaf.BGuid.Guid ActiveCompanyId
		{
			get
			{
				Debug.Assert(CurrentUser.get("activeCompanyId") != null);
				Debug.Assert(CurrentUser.get("activeCompanyId") is Swaf.BGuid.Guid);
				return (Swaf.BGuid.Guid)CurrentUser.get("activeCompanyId");
			}
		}

		public IBizObj ActiveCompany
		{
			get
			{
				return App.ruleMgr.execute("getCachedCompanyInfo", ActiveCompanyId).CallResults as IBizObj;
			}
		}

		public string getThemeDir(string name) { return String.Format("/App_Themes/{0}/{1}", Theme, name); }

		public Swaf.IApplication App { get { return Swaf.Application.currentApp; } }
		public void BizProcCreated(ActiveBizProc bizProc) { }
		public void WidgetCreated(ActiveWidget widget)
		{
			if (((System.Web.UI.Control)widget.Widget).Page == null)
			{
				System.Web.UI.Control pageForm = FindControl("form1");
				if(pageForm != null)
					pageForm.Controls.Add(widget.Widget as System.Web.UI.Control);
			}
		}

	}

	/// <summary>This attribute indicates that a given method or property is localizable and
	/// should have its value localized for the specific culture of the current user.  This 
	/// localization attribute is different from the System.ComponentModel.LocalizableAttribute
	/// in that it also allows a key to be specified that can assist in localizing the property.
	/// </summary>
	/// <remarks>The key for a localizable attribute is used by CSF's localization manager
	/// to locate the given localized text.  If the key is not specified, then the key would
	/// need to be calculated based on the context of the property/method.</remarks>
	[AttributeUsage(AttributeTargets.Class)]
	public class SecurityAttribute : Attribute
	{
		public string ResourceName = "";
		public Privileges RequiredAccess = Privileges.None;
		public SecurityAttribute(string resourceName, Privileges requiredAccess)
		{
			ResourceName = resourceName;
			RequiredAccess = requiredAccess;
		}
	}
}
